﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using System.Windows.Markup;

namespace ZX_AAA_Workshop
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window, INotifyPropertyChanged
  {
    public Window1()
    {
      var settings = LoadSettings() ?? GetDefaultSettings();
      this.settings = settings;

      InitializeComponent();
      this.Snapshots.Items.Clear();

      this.SavePath.Text = Environment.CurrentDirectory;

      RefreshSnapshots();

      //var timer = new System.Windows.Threading.DispatcherTimer();
      //timer.Tick += delegate
      //{
      //  RefreshSnapshots();
      //};
      //timer.Start();
    }
    Settings settings;

    public Settings Settings
    {
      get { return settings; }
    }

    static Settings LoadSettings()
    {
      try
      {
        var path = GetSettingsPath();
        if (System.IO.File.Exists(path))
        {
          var xml = System.IO.File.ReadAllText(path);
          var settings = (Settings)XamlReader.Parse(xml);
          return settings;
        }
      }
      catch (Exception exc)
      {
        Trace.WriteLine(exc);
      }
      return null;
    }
    static void SaveSettings(Settings settings)
    {
      try
      {
        var path = GetSettingsPath();
        var xml = XamlHlp.SaveToXamlText(settings);
        System.IO.File.WriteAllText(path, xml);
      }
      catch (Exception exc)
      {
        Trace.WriteLine(exc);
      }
    }
    static string GetSettingsPath()
    {
      var appPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
      var settingsPath = System.IO.Path.ChangeExtension(appPath, "config");
      return settingsPath;
    }
    static Settings GetDefaultSettings()
    {
      //var settings = new Settings
      //{
      //  SnapshotDefinitions = new ObservableCollection<SnapshotDefinition>(snapshotDefinitions),
      //};
      var settings = (Settings)XamlReader.Parse(Properties.Resources.DefaultSettings);
      return settings;
    }

    Snapshot[] RefreshSnapshots()
    {
      try
      {
        var definitions = this.SnapshotDefinitions
          .Concat(new[] { this.NewEmulatorParameters })
          .Where(def => def != null);
        var snapshots = MakeSnapshots(this.WithBorder.IsChecked == true, definitions);
        this.Snapshots.ItemsSource = snapshots;
        return snapshots;
      }
      catch (Exception exc)
      {
        OnError(exc);
        return Array<Snapshot>.Empty;
      }
    }

    void OnError(Exception exc)
    {
      Trace.WriteLine(exc);
      MessageBox.Show(exc.ToString(), "ZX AAA Workshop");
    }

    private void FindEmulatorButton_Click(object sender, RoutedEventArgs e)
    {
      RefreshSnapshots();
      //var processes = Process.GetProcessesByName(@"spectaculator");
      //this.Emulators.ItemsSource = processes;
    }

    private void SnapshotButton_Click(object sender, RoutedEventArgs e)
    {
      var snapshots = RefreshSnapshots();

      foreach (var snapshot in snapshots)
      {
        try
        {
          var imageFormatString = settings.ImageFormat ?? "png";
          var imageFormat = GetImageFormat(imageFormatString);
          var extension = GetImageFileExtension(imageFormat);
          var savePath = FindSavePath(this.SavePath.Text, string.Format("{0}.{1}", snapshot.FileName, extension));
          FileHlp.EnsureDirectory(System.IO.Path.GetDirectoryName(savePath));

          if (this.Settings.IsManualFileName)
          {
            savePath = ChooseFileName(savePath, imageFormatString);
            if (savePath == null)
              continue;
            this.SavePath.Text = System.IO.Path.GetDirectoryName(savePath);
            imageFormat = this.ImageFormats.Select(_imageFormatString => GetImageFormat(_imageFormatString))
              .FirstOrDefault(_imageFormat => savePath.ToLowerInvariant().EndsWith(GetImageFileExtension(_imageFormat)))
                ?? System.Drawing.Imaging.ImageFormat.Png;
          }

          snapshot.Bitmap.Save(savePath, imageFormat);
        }
        catch (Exception exc)
        {
          OnError(exc);
        }
      }
    }

    string ChooseFileName(string savePath, string imageFormatString)
    {
      using (var dialog = new System.Windows.Forms.SaveFileDialog())
      {
        dialog.FileName = System.IO.Path.GetFileNameWithoutExtension(savePath);
        dialog.Filter = this.ImageFormats
          .Select(format =>
            {
              var imageFormat = GetImageFormat(format);
              var extension = GetImageFileExtension(imageFormat);
              return string.Format("{1} файл (*.{0})|*.{0}", extension, imageFormat.ToString().ToUpperInvariant());
            })
          .Concat(new[] { "Все файлы (*.*)|*.*" })
          .JoinToString("|");
        dialog.FilterIndex = Array.IndexOf(this.ImageFormats, imageFormatString) + 1;
        dialog.AddExtension = true;
        dialog.AutoUpgradeEnabled = true;
        dialog.OverwritePrompt = true;
        dialog.SupportMultiDottedExtensions = true;
        dialog.ValidateNames = true;
        dialog.Title = "Задай имя файла";
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
          return dialog.FileName;
        }
        return null;
      }
    }

    static System.Drawing.Imaging.ImageFormat GetImageFormat(string imageFormatString)
    {
      var properties = typeof(System.Drawing.Imaging.ImageFormat).GetProperties(System.Reflection.BindingFlags.Static
        | System.Reflection.BindingFlags.Public);
      var imageFormat = properties.First(p => p.Name.ToLowerInvariant() == imageFormatString.ToLowerInvariant())
        .GetValue(null, Array<object>.Empty);
      return (System.Drawing.Imaging.ImageFormat)imageFormat;
    }
    static string GetImageFileExtension(System.Drawing.Imaging.ImageFormat imageFormat)
    {
      return imageFormat.ToString().ToLowerInvariant();
    }
    public string[] ImageFormats
    {
      get { return new[] { "png", "gif", "bmp", "tiff", "jpeg" }; }
    }

    static Snapshot[] MakeSnapshots(bool withBorder, IEnumerable<SnapshotDefinition> snapshotDefinitions)
    {
      //var processes = this.Emulators.SelectedItems.OfType<Process>().ToArray();
      var processes = snapshotDefinitions
        .Where(definition => !definition.ProcessName.IsNullOrEmpty())
        .GroupBy(definition => definition.ProcessName.ToLowerInvariant())
        .SelectMany(group =>
          Process.GetProcessesByName(group.Key)
          .Select(process =>
            new
            {
              Definitions = group.ToArray(),
              Process = process,
            }));

      var snapshots = processes
        .Select(processWithDefinitions =>
        {
          var process = processWithDefinitions.Process;
          try
          {
            var image = MakeSnapshot(process, processWithDefinitions.Definitions, withBorder);
            if (image == null)
              return null;
            return image;
          }
          catch (Exception exc)
          {
            Trace.Write(exc);
            return null;
          }
        })
        .Where(snapshot => snapshot != null)
        .ToArray();
      return snapshots;
    }

    static Snapshot MakeSnapshot(Process process, SnapshotDefinition[] snapshotDefinitions, bool withBorder)
    {
      var snapshot = new Snapshot
      {
        ProcessName = process.ProcessName,
      };

      IntPtr foundWindow;
      Int32Rect foundRect;
      SnapshotDefinition foundSnapshotDefinition;
      string foundMainWindowTitle;
      FindWindowForSnapshot(process, snapshotDefinitions, withBorder,
        out foundWindow, out foundRect, out foundSnapshotDefinition, out foundMainWindowTitle);

      if (foundWindow == IntPtr.Zero)
      {
        foundWindow = process.MainWindowHandle;
        foundRect = ScreenShotHlp.GetClientRect(foundWindow);
        foundSnapshotDefinition = snapshotDefinitions[0];
        foundMainWindowTitle = process.MainWindowTitle;
        snapshot.Comment = string.Format("Не найдено окно подходящего размера ({0}). Попробуй выбрать другой режим окна.",
          snapshotDefinitions.Select(def =>
            string.Format("{1}x{2}", def.WindowClassName, def.WindowRect.Width, def.WindowRect.Height).Trim())
          .JoinToString(" или "));

        if (foundRect.Width < MinWindowRect.Width || foundRect.Height < MinWindowRect.Height)
          return null;
      }

      var image = ScreenShotHlp.CaptureWindow(foundWindow, foundRect);
      var imageSource = image.ToBitmapSource();
      snapshot.Bitmap = image;
      snapshot.WpfBitmapSource = imageSource;
      snapshot.Definition = foundSnapshotDefinition;
      snapshot.WindowTitle = foundMainWindowTitle;

      var fileName = FindFileName(foundMainWindowTitle, foundSnapshotDefinition.WindowTitleFormat);
      snapshot.FileName = FileHlp.MakeValidFileName(fileName != null ?
            System.IO.Path.GetFileNameWithoutExtension(fileName) : snapshot.ProcessName).ToLowerInvariant();

      return snapshot;
    }
    static readonly Int32Rect MinWindowRect = new Int32Rect(0, 0, 256, 192);

    static void FindWindowForSnapshot(Process process, SnapshotDefinition[] snapshotDefinitions, bool withBorder,
      out IntPtr foundWindow, out Int32Rect foundRect, out SnapshotDefinition foundSnapshotDefinition, out string mainWindowTitle)
    {
      foundWindow = IntPtr.Zero;
      foundRect = new Int32Rect();
      foundSnapshotDefinition = null;
      mainWindowTitle = null;

      var processWindows = ScreenShotHlp.FindProcessWindows(process);
      var windowInfos = processWindows
        .Select(window =>
          new
          {
            Window = window,
            Rect = ScreenShotHlp.GetClientRect(window),
            Title = ScreenShotHlp.GetWindowText(window),
          });

      var matchedWindows = windowInfos.Join(snapshotDefinitions,
        windowInfo => windowInfo.Rect.Size(), snapshotDefinition => snapshotDefinition.WindowSize,
        (windowInfo, snapshotDefinition) => new { windowInfo, snapshotDefinition });

      var matchedWindow = matchedWindows.FirstOrDefault();
      if (matchedWindow != null)
      {
        var windowInfo = matchedWindow.windowInfo;
        var snapshotDefinition = matchedWindow.snapshotDefinition;

        var snapshotRect = withBorder ? snapshotDefinition.CalcWithBorderRect : snapshotDefinition.SnapshotRect;
        snapshotRect.X += windowInfo.Rect.X;
        snapshotRect.Y += windowInfo.Rect.Y;

        foundWindow = windowInfo.Window;
        foundRect = snapshotRect;
        foundSnapshotDefinition = snapshotDefinition;

        var ancestorInfos = ScreenShotHlp.EnumerateWindowAncestorsAndSelf(windowInfo.Window).Select(window =>
          new
          {
            Window = window,
            Rect = ScreenShotHlp.GetClientRect(window),
            Title = ScreenShotHlp.GetWindowText(window),
          });

        mainWindowTitle = ancestorInfos.Last().Title;
      }

      //foreach (var processWindow in processWindows)
      //{
      //  foreach (var snapshotDefinition in snapshotDefinitions)
      //  {
      //    //var window = snapshotDefinition.WindowClassName != null ?
      //    //  User32.FindWindowEx(processWindow, IntPtr.Zero, snapshotDefinition.WindowClassName, null)
      //    //  : processWindow;
      //    var window = processWindow;
      //    if (window == IntPtr.Zero)
      //      continue;

      //    var windowRect = ScreenShotHlp.GetClientRect(window);
      //    if (windowRect.Width != snapshotDefinition.WindowRect.Width ||
      //      windowRect.Height != snapshotDefinition.WindowRect.Height)
      //    {
      //      // HACK: при большом разрешении в Unreal урезается вертикальный размер
      //      if (!(windowRect.Width == snapshotDefinition.WindowRect.Width && snapshotDefinition.WindowRect.Height > 700))

      //        continue;
      //    }

      //    var snapshotRect = withBorder ? snapshotDefinition.CalcWithBorderRect : snapshotDefinition.SnapshotRect;
      //    snapshotRect.X += windowRect.X;
      //    snapshotRect.Y += windowRect.Y;

      //    foundWindow = window;
      //    foundRect = snapshotRect;
      //    foundSnapshotDefinition = snapshotDefinition;
      //    mainWindowTitle = ScreenShotHlp.GetWindowText(processWindow);
      //    return;
      //  }
      //}
    }

    static string FindFileName(string windowTitle, string titleFormat)
    {
      // titleFormat: {filename} - {}
      return FindField(windowTitle, titleFormat, "filename");
    }
    static string FindField(string str, string format, string fieldName)
    {
      // titleFormat: {filename} - {}
      var isSeparator = true;
      var strRight = str;
      var leftFieldName = (string)null;
      var separatorsAndFields = SplitIntoSeparatorsAndFields(format);
      foreach (var separatorOrField in separatorsAndFields)
      {
        if (isSeparator)
        {
          var separator = separatorOrField;

          if (leftFieldName == null)
          {
            if (!strRight.StartsWith(separator))
              return null;
          }

          var parts = strRight.Split(new[] { separator }, 2, StringSplitOptions.None);
          if (separatorOrField.Length > 0 && parts.Length == 1)
            return null;
          if (leftFieldName != null && leftFieldName == fieldName)
            return parts[0];
          strRight = parts.Length > 1 ? parts[1] : parts[0];
          isSeparator = false;
        }
        else
        {
          leftFieldName = separatorOrField;
          isSeparator = true;
        }
      }
      if (isSeparator)
      {
        if (leftFieldName != null && leftFieldName == fieldName)
          return strRight;
      }
      return null;
    }
    static IEnumerable<string> SplitIntoSeparatorsAndFields(string format)
    {
      if (format == null)
        yield break;
      var isSeparator = true;
      var start = 0;
      for (int i = 0; i < format.Length; i++)
      {
        var c = format[i];
        if (isSeparator)
        {
          if (c == '{')
          {
            if (i + 1 < format.Length && format[i + 1] != '{')
            {
              yield return format.Substring(start, i - start).Replace("{{", "{").Replace("}}", "}");
              isSeparator = false;
              start = i + 1;
            }
            else
              ++i;
          }
        }
        else
        {
          if (c == '}')
          {
            yield return format.Substring(start, i - start).Replace("{{", "{").Replace("}}", "}");
            isSeparator = true;
            start = i + 1;
          }
        }
      }
    }

    string FindSavePath(string dir, string name)
    {
      var nameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(name);
      var extension = System.IO.Path.GetExtension(name);
      var searchPattern = string.Format("{0}*{1}", nameWithoutExtension, extension);
      var files = System.IO.Directory.Exists(dir) ?
        System.IO.Directory.GetFiles(dir, searchPattern) : Array<string>.Empty;
      var lastIndex = files.Max(file =>
        {
          var _name = System.IO.Path.GetFileName(file);
          var indexString = _name.Substring(nameWithoutExtension.Length,
            _name.Length - nameWithoutExtension.Length - extension.Length);
          if (indexString == "")
            return 1;
          return ConvertHlp.ToInt(indexString);
        });
      var newIndex = lastIndex != null ? lastIndex.Value + 1 : 1;
      var newName = string.Format(newIndex == 1 ? "{0}{2}" : "{0}{1}{2}",
        nameWithoutExtension, newIndex, extension);
      var savePath = System.IO.Path.Combine(dir, newName);
      return savePath;
    }

    public ObservableCollection<SnapshotDefinition> SnapshotDefinitions
    {
      get { return this.settings.SnapshotDefinitions; }
    }

    // Преднастроенные эмуляторы теперь задаются в ресурсе DefaultSettings.config
    //static readonly ObservableCollection<SnapshotDefinition> snapshotDefinitions = new[]
    //{
    //  // border: Small
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 272, 208),
    //    SnapshotRect = new Int32Rect(8, 8, 256, 192),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Small",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 544, 416),
    //    SnapshotRect = new Int32Rect(16, 16, 512, 384),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Small",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 816, 624),
    //    SnapshotRect = new Int32Rect(24, 24, 768, 576),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Small",
    //  },

    //  // border: Medium
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 320, 256),
    //    SnapshotRect = new Int32Rect(32, 32, 256, 192),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Medium",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 640, 512),
    //    SnapshotRect = new Int32Rect(64, 64, 512, 384),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Medium",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 960, 768),
    //    SnapshotRect = new Int32Rect(96, 96, 768, 576),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Medium",
    //  },

    //  // border: Large
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 352, 288),
    //    SnapshotRect = new Int32Rect(48, 48, 256, 192),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Large",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 704, 576),
    //    SnapshotRect = new Int32Rect(96, 96, 512, 384),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Large",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Spectaculator",
    //    WindowClassName = "ZXClass",
    //    WindowRect = new Int32Rect(0, 0, 1056, 864),
    //    SnapshotRect = new Int32Rect(144, 144, 768, 576),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Large",
    //  },

    //  // border: None
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 256, 192),
    //    SnapshotRect = new Int32Rect(0, 0, 256, 192),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "None",
    // },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 512, 384),
    //    SnapshotRect = new Int32Rect(0, 0, 512, 384),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "None",
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 1024, 768),
    //    SnapshotRect = new Int32Rect(0, 0, 1024, 768),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "None",
    //  },

    //  // border: Small
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 320, 240),
    //    SnapshotRect = new Int32Rect(32, 24, 256, 192),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Small",
    //    WithBorderRect = new Int32Rect(16, 16, 288, 208),
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 640, 480),
    //    SnapshotRect = new Int32Rect(64, 48, 512, 384),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Small",
    //    WithBorderRect = new Int32Rect(32, 32, 576, 416),
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 1280, 960),
    //    SnapshotRect = new Int32Rect(128, 96, 1024, 768),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Small",
    //    WithBorderRect = new Int32Rect(64, 64, 1152, 832),
    //  },

    //  // border: Complete
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 384, 300),
    //    SnapshotRect = new Int32Rect(64, 64, 256, 192),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Complete",
    //    WithBorderRect = new Int32Rect(16, 32, 352, 256),
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 768, 600),
    //    SnapshotRect = new Int32Rect(128, 128, 512, 384),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Complete",
    //    WithBorderRect = new Int32Rect(32, 64, 704, 512),
    //  },
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Unreal",
    //    WindowClassName = null, //"EMUL_WND",
    //    WindowRect = new Int32Rect(0, 0, 1536, 1200),
    //    SnapshotRect = new Int32Rect(256, 256, 1024, 768),
    //    WindowTitleFormat = "{filename} - {}",
    //    Border = "Complete",
    //    WithBorderRect = new Int32Rect(64, 128, 1408, 1024),
    //  },

    //  // border: Normal
    //  new SnapshotDefinition
    //  {
    //    ProcessName = "Fuse",
    //    WindowClassName = null,
    //    WindowRect = new Int32Rect(0, 0, 320, 269),
    //    SnapshotRect = new Int32Rect(32, 24, 256, 192),
    //    Border = "Normal",
    //    WithBorderRect = new Int32Rect(0, 0, 320, 240),
    //  },
    //}._n(a => new ObservableCollection<SnapshotDefinition>(a));

    //public static readonly string[] SupportedEmulators = SnapshotDefinitions
    //  .GroupBy(def => def.ProcessName)
    //  .Select(group =>
    //    string.Format("{0} ({1})", group.Key,
    //      group.Select(def => string.Format("{0}x{1}", def.WindowRect.Width, def.WindowRect.Height))
    //      .JoinToString(", ")))
    //  .ToArray();

    private void BrowseButton_Click(object sender, RoutedEventArgs e)
    {
      using (var dlg = new System.Windows.Forms.FolderBrowserDialog())
      {
        dlg.Description = "Выбери папку куда сохранять снимки";
        dlg.SelectedPath = this.SavePath.Text;
        dlg.ShowNewFolderButton = true;
        var result = dlg.ShowDialog();
        if (result == System.Windows.Forms.DialogResult.OK)
        {
          this.SavePath.Text = dlg.SelectedPath;
        }
      }
    }

    private void WithBorder_Click(object sender, RoutedEventArgs e)
    {
      RefreshSnapshots();
    }

    private void EmulatorsButton_Click(object sender, RoutedEventArgs e)
    {
      if (this.EmulatorsButton.IsChecked == true)
      {
        BindingOperations.ClearBinding(this.snapshotPane, Grid.WidthProperty);
        this.scroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
        this.Width += this.emulatorsPane.ActualWidth;
      }
      else
      {
        this.scroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
        BindingOperations.SetBinding(this.snapshotPane, Grid.WidthProperty,
          new Binding("ActualWidth") { ElementName = "scroll" });
        this.Width -= this.emulatorsPane.ActualWidth;
      }
    }

    public SnapshotDefinition NewEmulatorParameters
    {
      get { return _NewEmulatorParameters; }
      set { _NewEmulatorParameters = value; NotifyPropertyChanged("NewEmulatorParameters"); }
    }
    SnapshotDefinition _NewEmulatorParameters;

    public static readonly string AppVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version
      ._n(version => string.Format("{0}.{1}", version.Major, version.Minor));

    private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
    {
      Mouse.Capture(this);
      try
      {
        this.finderFrame = new System.Windows.Threading.DispatcherFrame(true);
        //var finderCursor = CursorHlp.ConvertToCursor(this.Finder, 32, 32, new Point(0.5, 0.5));
        var finderCursor = CursorHlp.CreateCursor(this.Finder, new Point(0.5, 0.5));
        this.Cursor = finderCursor;
        this.Finder.Visibility = Visibility.Hidden;
        System.Windows.Threading.Dispatcher.PushFrame(finderFrame);
        this.RefreshSnapshots();
      }
      finally
      {
        this.Cursor = null;
        this.Finder.Visibility = Visibility.Visible;
        this.finderFrame = null;
        Mouse.Capture(null);
      }
    }
    System.Windows.Threading.DispatcherFrame finderFrame = null;

    private void Window_MouseMove(object sender, MouseEventArgs e)
    {
      if (this.finderFrame != null)
      {
        var position = e.GetPosition(this);
        var screenPos = this.PointToScreen(position);
        var window = User32.WindowFromPoint(new POINT { x = (int)screenPos.X, y = (int)screenPos.Y });
        int processId;
        User32.GetWindowThreadProcessId(window, out processId);
        if (processId != Process.GetCurrentProcess().Id)
        {
          var process = Process.GetProcessById(processId);
          var clientRect = ScreenShotHlp.GetClientRect(window);
          EnsureNewEmulatorParameters();
          var snapshotDefiniton = this.NewEmulatorParameters;
          snapshotDefiniton.ProcessName = process.ProcessName;
          snapshotDefiniton.WindowRect = new Int32Rect(0, 0, clientRect.Width, clientRect.Height);
          snapshotDefiniton.SnapshotRect = FindSnapshotRect(snapshotDefiniton.WindowRect);
          snapshotDefiniton.NotifyPropertyChanged(null);
        }
      }
    }
    static Int32Rect FindSnapshotRect(Int32Rect windowRect)
    {
      var normalSize = new System.Drawing.Size(256, 192);
      var factor = Enumerable.Range(1, 10)
        .LastOrDefault(i => normalSize.Width * i <= windowRect.Width && normalSize.Height * i <= windowRect.Height);
      if (factor == 0)
        return windowRect;

      var size = new System.Drawing.Size(normalSize.Width * factor, normalSize.Height * factor);

      return new Int32Rect(windowRect.X + (windowRect.Width - size.Width) / 2, windowRect.Y + (windowRect.Height - size.Height) / 2, size.Width, size.Height);
    }

    private void Window_MouseUp(object sender, MouseButtonEventArgs e)
    {
      if (this.finderFrame != null)
        this.finderFrame.Continue = false;
    }

    private void AddOk_Button_Click(object sender, RoutedEventArgs e)
    {
      OnAddComplete(true);
    }
    private void AddCancel_Button_Click(object sender, RoutedEventArgs e)
    {
      OnAddComplete(false);
    }
    void OnAddComplete(bool isOk)
    {
      if (isOk)
      {
        var snapshotDefinition = this.NewEmulatorParameters;
        if (snapshotDefinition.ProcessName.IsNullOrEmpty())
          return;
        this.SnapshotDefinitions.Add(snapshotDefinition);
        var container = this.EmulatorsList.ItemContainerGenerator.ContainerFromItem(snapshotDefinition);
        container.As<FrameworkElement>()._n(frameworkElement => frameworkElement.BringIntoView());
        SaveSettings(this.settings);
      }
      this.NewEmulatorParameters = null;
      this.AddEmulatorButton.IsChecked = false;
      RefreshSnapshots();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private void AddEmulatorButton_Checked(object sender, RoutedEventArgs e)
    {
      EnsureNewEmulatorParameters();
    }

    void EnsureNewEmulatorParameters()
    {
      if (this.NewEmulatorParameters == null)
      {
        this.NewEmulatorParameters = new SnapshotDefinition
          {
            WindowRect = MinWindowRect,
            SnapshotRect = MinWindowRect,
            WindowTitleFormat = "{filename} - {}",
          };
      }
    }

    private void imageFormatSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      var isChanged = e.RemovedItems.Count > 0; // HACK: такая вот проверка, чтобы не сохраняло при запуске
      if (isChanged)
        SaveSettings(this.Settings);
    }

    private void CheckBox_Click(object sender, RoutedEventArgs e)
    {
      SaveSettings(this.Settings);
    }
  }

  class Snapshot
  {
    public SnapshotDefinition Definition { get; set; }
    public string ProcessName { get; set; }
    public string WindowTitle { get; set; }
    public System.Drawing.Bitmap Bitmap { get; set; }
    public BitmapSource WpfBitmapSource { get; set; }
    public string FileName { get; set; }
    public string Comment { get; set; }

    public string SizeString
    //{ get; set; }
    {
      get
      {
        return Bitmap != null ?
          string.Format("{0}x{1}", Bitmap.Width, Bitmap.Height) : null;
      }
    }
  }

  [ContentProperty("SnapshotDefinitions")]
  public class Settings
  {
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ObservableCollection<SnapshotDefinition> SnapshotDefinitions
    {
      get { return _SnapshotDefinitions; }
      set { _SnapshotDefinitions = value; }
    }
    ObservableCollection<SnapshotDefinition> _SnapshotDefinitions = new ObservableCollection<SnapshotDefinition>();

    public string ImageFormat { get; set; }

    public bool IsManualFileName { get; set; }
  }
  public class SnapshotDefinition : INotifyPropertyChanged
  {
    [DefaultValue("")]
    public string ProcessName { get; set; }

    public System.Drawing.Size WindowSize
    {
      get { return WindowRect.Size(); }
      set { WindowRect = new Int32Rect(0, 0, value.Width, value.Height); NotifyPropertyChanged(null); }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Obsolete("Больше не используется")]
    public string WindowClassName { get; set; }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public Int32Rect WindowRect { get; set; }

    [DefaultValue(null)]
    public string Border { get; set; }

    public Int32Rect SnapshotRect { get; set; }

    [DefaultValue(null)]
    public Int32Rect? WithBorderRect { get; set; }

    [DefaultValue(null)]
    public string WindowTitleFormat { get; set; }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public Int32Rect CalcWithBorderRect
    {
      get { return WithBorderRect ?? WindowRect; }
      set { WithBorderRect = value; }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }


  class NegateBooleanConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      return object.Equals(value, false);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      return object.Equals(value, false);
    }
  }

  class NotEmptyToBooleanConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      return !System.Convert.ToString(value).IsNullOrEmpty();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      return null;
    }
  }

  static class EnumerableHlp
  {
    public static string JoinToString(this IEnumerable<string> e, string separator)
    {
      return string.Join(separator, e.ToArray());
    }
  }
  static class Hlp
  {
    public static T As<T>(this object o) where T : class
    {
      return o as T;
    }
    public static TResult _n<T, TResult>(this T o, Func<T, TResult> getter)
    {
      if (o != null)
        return getter(o);
      return default(TResult);
    }
    public static void _n<T>(this T o, Action<T> action)
    {
      if (o != null)
        action(o);
    }
  }
  static class RectHlp
  {
    public static System.Drawing.Size Size(this Int32Rect rect)
    {
      return new System.Drawing.Size(rect.Width, rect.Height);
    }
  }
}
